/**
 * fshows.com
 * Copyright (C) 2013-2020 All Rights Reserved.
 */
package com.fshows.leshuapay.sdk.client.impl;

import com.fshows.leshuapay.sdk.client.LeshuaPayClient;
import com.fshows.leshuapay.sdk.exception.LeshuaException;
import com.fshows.leshuapay.sdk.request.pay.LeshuaBasePayRequest;
import com.fshows.leshuapay.sdk.response.pay.LeshuaBasePayResponse;
import com.fshows.leshuapay.sdk.util.FsHttpUtil;
import com.fshows.leshuapay.sdk.util.LeshuaSignature;
import com.fshows.leshuapay.sdk.util.LeshuaUtils;
import com.fshows.leshuapay.sdk.util.RandomStringGenerator;
import com.fshows.leshuapay.sdk.util.ValidateUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.util.HashSet;
import java.util.Map;
import java.util.Set;


/**
 * @author zhaoyi
 * @version LeshuaPayClientImpl.java, v 0.1 2020-07-28 08:50 AM zhaoyi
 */
@Slf4j
@Data
public class LeshuaPayClientImpl<Req extends LeshuaBasePayRequest, Res extends LeshuaBasePayResponse> implements LeshuaPayClient<Req, Res> {
    /**
     * 乐刷常量
     */
    private static final String LESHUA = "leshua";

    /**
     * XML标签
     */
    private static final String XML_SIGN = "<sign>";

    /**
     * 交易秘钥
     */
    private String apiKey;

    /**
     * 交易服务地址
     */
    private String payServerUrl;

    /**
     * 超时设置，10秒
     */
    private Integer timeout = 10000;

    private FsHttpUtil httpClientHelper = new FsHttpUtil();

    public LeshuaPayClientImpl(String apiKey, String payServerUrl) {
        this.apiKey = apiKey;
        this.payServerUrl = payServerUrl;
    }

    public LeshuaPayClientImpl(String apiKey, String payServerUrl, Integer timeout) {
        this.apiKey = apiKey;
        this.payServerUrl = payServerUrl;
        this.timeout = timeout;
    }


    /**
     * 默认超时时间-10秒
     *
     * @param reqData
     * @param resClass
     * @return
     * @throws Exception
     */
    @Override
    public Res execute(Req reqData, Class<Res> resClass) throws Exception {
        return this.execute(reqData, apiKey, payServerUrl, resClass, timeout);
    }

    /**
     * 自定义超时时间
     *
     * @param reqData
     * @param resClass
     * @param timeout
     * @return
     * @throws Exception
     */
    @Override
    public Res execute(Req reqData, Class<Res> resClass, Integer timeout) throws Exception {
        return this.execute(reqData, apiKey, payServerUrl, resClass, timeout);
    }

    /**
     * 调用乐刷交易接口
     *
     * @param reqData
     * @param apiKey
     * @param resClass
     * @return
     * @throws Exception
     */
    @Override
    public Res execute(Req reqData, String apiKey, String payServerUrl, Class<Res> resClass, Integer timeout) throws Exception {
        ValidateUtil.validateWithThrow(reqData);
        String respXml = this.request(payServerUrl, reqData, apiKey, true, timeout);
        return LeshuaUtils.getObjectFromXML(respXml, resClass, LESHUA);
    }

    /**
     * 调用乐刷支付接口，返回xml格式响应字符串
     *
     * @param serviceUrl
     * @param request
     * @param apiKey
     * @param isCheckRespSign
     * @param timeout
     * @return
     * @throws Exception
     */
    private String request(String serviceUrl, Req request, String apiKey, boolean isCheckRespSign, Integer timeout) throws Exception {
        long costTimeStart = System.currentTimeMillis();
        // 设置随机字符串，不长于32 位
        String nonceStr = RandomStringGenerator.genUUIDString();
        Map<String, String> reqMap = LeshuaUtils.toMap(request);
        reqMap.put("nonce_str", nonceStr);
        // 根据API给的签名规则进行签名
        String sign = LeshuaSignature.getMD5Sign(reqMap, reqExcludedSignParams(), apiKey);
        reqMap.put("sign", sign);
        String respStr = httpClientHelper.postForm(serviceUrl, reqMap, FsHttpUtil.ACCEPT_TYPE_XML, timeout);
        long totalTimeCost = System.currentTimeMillis() - costTimeStart;
        // 打印回包数据
        log.info("直连乐刷 >> 耗时={}ms, request={}, response={}", totalTimeCost, reqMap, respStr);
        if (isCheckRespSign) {
            if (null != respStr && respStr.contains(XML_SIGN) && !LeshuaSignature.checkIsSignValidFromResponseString(respStr, resExcludedSignParams(), apiKey)) {
                log.error("验签失败,request={}", request);
                throw new LeshuaException("验签失败");
            }
        }
        return respStr;
    }

    /**
     * 请求参数不参与计算签名的参数
     *
     * @return
     */
    private Set<String> reqExcludedSignParams() {
        Set<String> excludedSignParams = new HashSet<>(1);
        excludedSignParams.add("sign");
        return excludedSignParams;
    }

    /**
     * 响应结果不参与计算签名的参数
     *
     * @return
     */
    private Set<String> resExcludedSignParams() {
        Set<String> excludedSignParams = new HashSet<>(2);
        excludedSignParams.add("sign");
        excludedSignParams.add("resp_code");
        return excludedSignParams;
    }

}
